<?php

namespace app\common\model;

use think\model\relation\HasOne;
require_once VENDOR_PATH. '/Wxpay/lib/WxPay.Api.php';//路径不可更改 否则回调时会报错
require_once VENDOR_PATH. '/Wxpay/example/WxPay.JsApiPay.php';
require_once VENDOR_PATH. "/Wxpay/lib/WxPay.Exception.php";
require_once VENDOR_PATH. "/Wxpay/lib/WxPay.Data.php";
use think\Model;


class Wechatpay extends Model {

    const APPID = 'wxa9aca80c516de89d';
    const MCHID = '1681414405';
    const KEY = '52c69e3a57331081823331c4e69d3f2e';
    const APPSECRET = '463cc0c309e94a0e6af31d00660d053e';
    //=======【证书路径设置】=====================================
    /**
     * TODO：设置商户证书路径
     * 证书路径,注意应该填写绝对路径（仅退款、撤销订单时需要，可登录商户平台下载，
     * API证书下载地址：https://pay.weixin.qq.com/index.php/account/api_cert，下载之前需要安装商户操作证书）
     * @var path
     */
    const SSLCERT_PATH = VENDOR_PATH.'/Wxpay/cert/apiclient_cert.pem';
    const SSLKEY_PATH =  VENDOR_PATH.'/Wxpay/cert/apiclient_key.pem';

    //=======【curl代理设置】===================================
    /**
     * TODO：这里设置代理机器，只有需要代理的时候才设置，不需要代理，请设置为0.0.0.0和0
     * 本例程通过curl使用HTTP POST方法，此处可修改代理服务器，
     * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0，此时不开启代理（如有需要才设置）
     * @var unknown_type
     */
    const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";
    const CURL_PROXY_PORT = 0;//8080;
    const REPORT_LEVENL = 1;




    //=======【上报信息配置】===================================
    /**
     * TODO：接口调用上报等级，默认紧错误上报（注意：上报超时间为【1s】，上报无论成败【永不抛出异常】，
     * 不会影响接口调用流程），开启上报之后，方便微信监控请求调用的质量，建议至少
     * 开启错误上报。
     * 上报等级，0.关闭上报; 1.仅错误出错上报; 2.全量上报
     * @var int
     */


    /**
     * 申请退款
     *
     * @param [type] $out_trade_no  商户订单号
     * @param [type] $out_refund_no 退款单号
     * @param [type] $total_fee 订单总金额
     * @param [type] $refund_fee    退款金额
     * @return void
     */

    public function refund($out_trade_no, $out_refund_no, $total_fee, $refund_fee,$desc="取消订单")
    {
        $post['appid'] = self::APPID;
        $post['mch_id'] = self::MCHID;
        $post['nonce_str'] = getRandom(32); //随机字符串
        $post['out_trade_no'] = $out_trade_no;  //商户订单号
        $post['out_refund_no'] = $out_refund_no;  //退款单号
        $post['total_fee'] = $total_fee * 100;  //订单总金额
        $post['refund_fee'] = $refund_fee * 100;  //退款金额
        $post['sign_type'] = 'MD5';  //退款金额
        $post['refund_desc'] = $desc;
        $sign = $this->getParam($post); //签名
        $post['sign'] = $sign;
        $post_xml = $this->ToXml($post);
        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
        $xml = $this->postXmlSSLCurl($post_xml, $url);
        $array = $this->FromXml($xml); //全要大写
        return $array;
    }


    /**
     * 退款查询
     * @param $refund_no  '单号'
     * @param $refund_type 'refund_id'=微信退款单号,'out_refund_no'=商户退款单号,'transaction_id'=微信订单号,'out_trade_no'=商户订单号  4选1 查询
     * @param $offset
     * @return mixed
     */
    public function refund_query($refund_no, $refund_type='refund_id',$offset=0)
    {

        $post['appid'] = self::APPID;
        $post['mch_id'] = self::MCHID;
        $post['nonce_str'] = getRandom(32); //随机字符串
        $post[$refund_type] = $refund_no; // 微信退款单号
        $post['sign_type'] = 'MD5';  //退款金额

        if($offset){
            $post['offset'] = $offset;
        }

        $sign = $this->getParam($post); //签名
        $post['sign'] = $sign;
        $post_xml = $this->ToXml($post);
        $url = 'https://api.mch.weixin.qq.com/pay/refundquery';
        $xml = $this->postXmlSSLCurl($post_xml, $url);
        return $this->FromXml($xml); //全要大写

    }
    ##############################################  微信支付公用方法  #############################################

    //对参数排序
    public function getParam($paramArray, $symbol = '=', $split = '&', $isencode = false)
    {
        $paramStr = '';
        ksort($paramArray);
        $i = 0;
        foreach ($paramArray as $key => $value) {
            if ($key == 'Signature') {
                continue;
            }
            if ($i == 0) {
                $paramStr .= '';
            } else {
                $paramStr .= "$split";
            }
            $paramStr .= $key . "$symbol" . ($isencode ? urlencode($value) : $value);
            ++$i;
        }
        $stringSignTemp = $paramStr . "&key=" . self::KEY;
        //dump($stringSignTemp);exit;
        $sign = strtoupper(md5($stringSignTemp));
        return $sign;
    }

    //curl请求
    private function http_request($url, $data = null, $headers = array())
    {
        $curl = curl_init();
        if (count($headers) >= 1) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

    private function postXmlSSLCurl($xml, $url, $second = 30)
    {
        $ch = curl_init();
        //超时时间
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //设置证书
        //使用证书：cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, self::SSLCERT_PATH);
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, self::SSLKEY_PATH);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error" . "<br>";
            curl_close($ch);
            return false;
        }
    }

    /**
     * 输出xml字符
     * @throws WxPayException
     **/
    private function ToXml($array = [])
    {
        $xml = "<xml>";
        foreach ($array as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 将xml转为array
     * @param string $xml
     * @throws WxPayException
     */
    private function FromXml($xml)
    {
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $this->values;
    }

}